#include "node.h"
#include "context.h"
#include "error.h"
#include <set>

Node::Node(const unsigned n, SimulationContext *c, double b, double l) : 
    number(n), context(c), bw(b), lat(l) 
{}

Node::Node() 
{ throw GeneralException(); }

Node::Node(const Node &rhs) : 
  number(rhs.number), context(rhs.context), bw(rhs.bw), lat(rhs.lat) {}

Node & Node::operator=(const Node &rhs) 
{
  return *(new(this)Node(rhs));
}

void Node::SetNumber(const unsigned n) 
{ number=n;}

unsigned Node::GetNumber() const 
{ return number;}

void Node::SetLatency(const double l)
{ lat=l;}

double Node::GetLatency() const 
{ return lat;}

void Node::SetBW(const double b)
{ bw=b;}

double Node::GetBW() const 
{ return bw;}

Node::~Node()
{}

// Implement these functions  to post an event to the event queue in the event simulator
// so that the corresponding node can recieve the ROUTING_MESSAGE_ARRIVAL event at the proper time
void Node::SendToNeighbors(const RoutingMessage *m)
{

    deque<Node*> * neighbors = GetNeighbors();
    deque<Node*>::iterator itr;
	 for(itr = neighbors->begin(); itr != neighbors->end(); ++itr){
					const RoutingMessage * l = new RoutingMessage(*m);
					SendToNeighbor(*itr,l);
		}



}

void Node::SendToNeighbor(const Node *n, const RoutingMessage *m)
{
	Link * temp = new Link();
	temp->SetSrc(this->number);
	temp->SetDest(n->number);
	//cerr << "Sending from:" << number << " to  " << n->number << " on " << *m << endl;

	const Link * l = new Link(*temp);
	Link * real = context->FindMatchingLink(l);
	if(real != 0){
	 Event * event = new Event(context->GetTime()+real->GetLatency(),10,n,m);
	 context->PostEvent(event);

	}
	 delete l;
	 delete temp;
}

deque<Node*> *Node::GetNeighbors()
{
  return context->GetNeighbors(this);
}

void Node::SetTimeOut(const double timefromnow)
{
  context->TimeOut(this,timefromnow);
}


bool Node::Matches(const Node &rhs) const
{
  return number==rhs.number;
}


#if defined(GENERIC)
void Node::LinkHasBeenUpdated(const Link *l)
{
  cerr << *this << " got a link update: "<<*l<<endl;
  //Do Something generic:
  //SendToNeighbors(new RoutingMessage);
}


void Node::ProcessIncomingRoutingMessage(const RoutingMessage *m)
{
  //cerr << *this << " got a routing message: "<<*m<<" Ignored "<<endl;
}


void Node::TimeOut()
{
  cerr << *this << " got a timeout: ignored"<<endl;
}

Node *Node::GetNextHop(const Node *destination) const
{
  return 0;
}

Table *Node::GetRoutingTable() const
{
  return 0;
}


ostream & Node::Print(ostream &os) const
{
  os << "Node(number="<<number<<", lat="<<lat<<", bw="<<bw<<")";
  return os;
}

#endif

#if defined(LINKSTATE)


void Node::LinkHasBeenUpdated(const Link *l)
{
  int age = t.updateLink(l);
  const RoutingMessage * message = new RoutingMessage(age, l->GetLatency(), l->GetSrc(), l->GetDest());
  SendToNeighbors(message);


}


void Node::ProcessIncomingRoutingMessage(const RoutingMessage *m)
{
  //err << *this << " Routing Message: "<<*m<<endl;;
	t.printTable();
	if(t.updateTable(m)){
		SendToNeighbors(m);
	}

}

void Node::TimeOut()
{
  cerr << *this << " got a timeout: ignored"<<endl;
}


Node *Node::GetNextHop(const Node *destination) const
{
	set<int> workingSet;
	set<int> other;
	map<int, int> parents;//Used to recursively find final table
	map<int, int> routing;
 	map<int, TopoLink >::const_iterator itr; //Used to iterate
 	map<int, int>::const_iterator parentItr;
 	map<int, TopoLink> adjacent;//Will be used to get adjacent nodes

 	//Add myself to the list
 	workingSet.insert(number);

 	//Add all adjacent nodes to other set.
 	adjacent = t.m[number];
 	for(itr = adjacent.begin(); itr != adjacent.end(); itr++){
 		other.insert(itr->first);
 		t.m[number][itr->first].distance =t.m[number][itr->first].cost;
 		t.m[number][itr->first].inSet = true;
 		parents[itr->first] = number;//Parent of the other node is this one.
 	}
 	//main part of djikstra
 	while(other.size()>0){
 		int src = 0;
 		int dest = 0;
 		t.find(&src, &dest);
 		if(workingSet.count(dest) > 0){
 			t.m[src][dest].inSet = false;
 			continue;
 		}
 		workingSet.insert(dest);
 		parents[dest] = src;
 		other.erase(dest);
 		adjacent = t.m[dest];
 	 	for(itr = adjacent.begin(); itr != adjacent.end(); itr++){
 	 		if(workingSet.count(itr->first) <= 0){
 	 			other.insert(itr->first);
 	 			int otherDist = t.m[dest][itr->first].distance;
 	 			int distEdge =t.m[dest][itr->first].cost+ t.m[src][dest].distance;
 	 			t.m[dest][itr->first].distance = min((int)(t.m[dest][itr->first].distance),(int)(t.m[dest][itr->first].cost+ t.m[src][dest].distance));
 	 			int finalDist = t.m[dest][itr->first].distance;
 	 			t.m[dest][itr->first].inSet = true;
 	 		}
 	 		parents[itr->first] = number;//Parent of the other node is this one.
 	 	}
 	}
 	for(parentItr = parents.begin(); parentItr!=parents.end(); parentItr++){
 		int parent = parentItr->second;
 		int curr = parentItr->first;
 		while(parent != number){
 			curr = parent;
 			parent = parents[parent];
 		}
 		routing[parentItr->first] = curr;
 	}
 	Node * a = new Node(routing[destination->number], NULL, 0, 0);
 	Node * realNode = context->FindMatchingNode(const_cast<Node *>(a));


 	return realNode;
}
int min(int first, int second){
	if(first == -1){
		return second;
	}
	else if(second == -1){
		return first;
	}
	else if(first < second){
		return first;
	}
	else{
		return second;
	}
}
Table *Node::GetRoutingTable() const
{
  // WRITE
  return 0;
}


ostream & Node::Print(ostream &os) const
{
  os << "Node(number="<<number<<", lat="<<lat<<", bw="<<bw<<")";
  return os;
}
#endif


#if defined(DISTANCEVECTOR)

void Node::LinkHasBeenUpdated(const Link *l)
{

	bool newShort = dist.updateLink(l);
	if(newShort){
		cerr << *this<<": Link Update (neighbors notified): "<<*l<<endl;
		const RoutingMessage * message = new RoutingMessage(l->GetSrc(), dist.shortest);
		SendToNeighbors(message);
	}
	else{
		cerr << *this<<": Link Update (neighbors NOT notified): "<<*l<<endl;
	}
}


void Node::ProcessIncomingRoutingMessage(const RoutingMessage *m)
{
	if(dist.updateTable(m)){
		cerr << "SENDING TO NEIGHBORS MY NEW UPDATED SHIZZLE" << endl;
		SendToNeighbors(m);
	}
}

void Node::TimeOut()
{
  cerr << *this << " got a timeout: ignored"<<endl;
}


Node *Node::GetNextHop(const Node *destination) const
{
	// do we need a check here to see if there is a best that has been found for this destination
	// or can we assume it has?
	Node * a = new Node(dist.shortest[destination->number].hop, NULL, 0, 0);
	Node * realNode = context->FindMatchingNode(const_cast<Node *>(a));
	return realNode;
}

Table *Node::GetRoutingTable() const
{
	return NULL;
}


ostream & Node::Print(ostream &os) const
{
  os << "Node(number="<<number<<", lat="<<lat<<", bw="<<bw;
  return os;
}
#endif
